home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / Winter Shell 1.0d2 / Source / Libraries / MenuLib / MenuLib.c next >
Encoding:
C/C++ Source or Header  |  1994-01-11  |  14.5 KB  |  527 lines  |  [TEXT/KAHL]

  1. /* Library for handling menus and the menu bar.
  2.  
  3.     Revision History:
  4.     
  5.     94/01/09 aih
  6.     - changed command constant prefix from "MC_" to "CMD_"
  7.     
  8.     93/12/25 aih
  9.     - imported function for outlining the size menu from TextMenuLib.c
  10.     
  11.     93/12/17 aih
  12.     - made menu table a handle so can dynamically append commands to it
  13.     
  14.     93/12/02 aih
  15.     - improved efficiency of menu adjustment
  16.     
  17.     93/03/18 AIH
  18.     - To speed things up, made menu table index by menu number
  19.     - To speed things up, the menu handle is saved in the menu table
  20.     
  21.     93/03/12 AIH
  22.     - Added functions to replace GetMHandle and to setup the help menu
  23.     
  24.     92/03/06 AIH
  25.     - Added help menu command
  26.     
  27.     92/02/25 AIH
  28.     - Added functions to map from menu IDs and item numbers to
  29.     menu command numbers
  30.     
  31.     92/02/21 AIH
  32.     - Added function to check menus by name
  33.     
  34.     91/11/15 AIH
  35.     - Uses the calculation (sizeof(long) * CHAR_BIT) to determine the
  36.     number of bits in a long integer instead of hard-coding the number
  37.     32.
  38.     
  39.     91/05/14 AIH
  40.     - Added function to determine if a menu item is enabled
  41.     
  42.     91/04/21 AIH
  43.     - Fixed menu validation under system 7.0
  44.     
  45.     91/04/06 AIH
  46.     - Added function to create a menu bar and insert all heirarchical menus
  47.     when the menu bar is created
  48.     
  49.     91/03/10 AIH
  50.     - Added functions for enabling/disabling the entire menu bar
  51.     
  52.     91/02/15 AIH
  53.     - Added functions for accessing the menu list
  54.     - Added constants for standard menus and menu items
  55.     
  56.     91/01/05 Ari Halberstadt (AIH)
  57.     - Inserted this standard header in all files */
  58.  
  59. #include <string.h>
  60. #include <limits.h>
  61. #include <Balloons.h>
  62. #include "KeyLib.h"
  63. #include "LowMemLib.h"
  64. #include "MacLib.h"
  65. #include "MathLib.h"
  66. #include "MemoryLib.h"
  67. #include "MenuLib.h"
  68. #include "ResourceConstantsLib.h"
  69. #include "ResourceLib.h"
  70. #include "ScreenLib.h"
  71. #include "StringLib.h"
  72.  
  73. static MenuPickType **gMenuTable;/* menu command translation table */
  74. static short gMenuTableSize;        /* number of commands in menu table (actually,
  75.                                                 value of largest command plus 1) */
  76.  
  77. /*---------------------------------------------------------------------------*/
  78. /* validation */
  79. /*---------------------------------------------------------------------------*/
  80.  
  81. /* true if the menu is a valid handle to a menu; in system 7.0 we can't
  82.     validate the Apple menu since it's in some weird heap zone */
  83. Boolean MenuValid(MenuHandle menu)
  84. {
  85.     if (menu && menu == GetMHandle(RLM_APPLE)) return(true);
  86.     return(HandleValidSize(menu, sizeof(MenuInfo) - sizeof(Str255)));
  87. }
  88.  
  89. /* true if the ID corresponds to the menu ID of an existing menu */
  90. Boolean MenuIDValid(short id)
  91. {
  92.     return(id != 0 && MenuHandleGet(id) != NULL);
  93. }
  94.  
  95. /* true if the item is a valid item in the menu with the specified ID */
  96. Boolean MenuItemValid(short id, short item)
  97. {
  98.     return(MenuIDValid(id) && 0 <= item && item <= CountMItems(MenuHandleGet(id)));
  99. }
  100.  
  101. /* true if the menu command is valid */
  102. Boolean MenuCmdValid(MenuCommandType cmd)
  103. {
  104.     return(0 <= cmd && cmd < gMenuTableSize);
  105. }
  106.  
  107. /* true if the menu pick is valid */
  108. Boolean MenuPickValid(const MenuPickType *pick)
  109. {
  110.     if (pick->menu && ! MenuValid(pick->menu)) return(false);
  111.     if (! MenuIDValid(pick->id)) return(false);
  112.     if (! MenuCmdValid(pick->cmd)) return(false);
  113.     return(true);
  114. }
  115.  
  116. /*---------------------------------------------------------------------------*/
  117. /* Menu commands are handled by using a table containing a mapping
  118.     of all menu ID and item numbers to unique menu command numbers.
  119.     The application passes a specific table, and these functions
  120.     handle the mapping between them. The menu table passed by the
  121.     application is terminated by an entry with a menu ID of zero. */
  122. /*---------------------------------------------------------------------------*/
  123.  
  124. /*---------------------------------------------------------------------------*/
  125. /* menu table maintenance */
  126. /*---------------------------------------------------------------------------*/
  127.  
  128. static void MenuSetup(void)
  129. {
  130.     MenuHandle help = MenuHandleGet(kHMHelpMenuID);
  131.     Str255 title;
  132.     short cmd;
  133.     
  134.     /* move the help menu command to the help menu */
  135.     if (MenuCmdValid(CMD_HELP) && MenuCmdHandle(CMD_HELP)) {
  136.         if (help && help != MenuCmdHandle(CMD_HELP)) {
  137.             GetItem(MenuCmdHandle(CMD_HELP), MenuCmdItem(CMD_HELP), title);
  138.             GetItemCmd(MenuCmdHandle(CMD_HELP), MenuCmdItem(CMD_HELP), &cmd);
  139.             DelMenuItem(MenuCmdHandle(CMD_HELP), MenuCmdItem(CMD_HELP));
  140.             AppendMenu(help, title);
  141.             (*gMenuTable)[CMD_HELP].item = CountMItems(help);
  142.             (*gMenuTable)[CMD_HELP].id = kHMHelpMenuID;
  143.             (*gMenuTable)[CMD_HELP].menu = help;
  144.             SetItemCmd(MenuCmdHandle(CMD_HELP), MenuCmdItem(CMD_HELP), cmd);
  145.         }
  146.     }
  147. }
  148.  
  149. /* append the commands to the menu table */
  150. void MenuTableAppend(const MenuPickType *mt)
  151. {
  152.     MenuPickType pick;
  153.     short i, maxcmd;
  154.     
  155.     maxcmd = 0;
  156.     for (i = 0; mt[i].id; i++)
  157.         if (mt[i].cmd > maxcmd)
  158.             maxcmd = mt[i].cmd;
  159.     maxcmd++;
  160.     if (! gMenuTable)
  161.         gMenuTable = HandleBeginClear(sizeof(MenuPickType) * maxcmd);
  162.     else {
  163.         maxcmd = max(gMenuTableSize, maxcmd);
  164.         HandleSizeSet(gMenuTable, sizeof(MenuPickType) * maxcmd);
  165.     }
  166.     gMenuTableSize = maxcmd;
  167.     for (i = 0; mt[i].id; i++) {
  168.         check(MenuPickValid(&mt[i]));
  169.         pick = mt[i];
  170.         pick.menu = MenuHandleGet(mt[i].id);
  171.         (*gMenuTable)[mt[i].cmd] = pick;
  172.         check(MenuPickValid(&pick));
  173.         check(MenuValid(pick.menu));
  174.     }
  175.     MenuSetup();
  176. }
  177.  
  178. /* set the menu table */
  179. void MenuTableSet(const MenuPickType *mt)
  180. {
  181.     if (gMenuTable) {
  182.         HandleEnd(gMenuTable);
  183.         gMenuTable = NULL;
  184.         gMenuTableSize = 0;
  185.     }
  186.     MenuTableAppend(mt);
  187. }
  188.  
  189. /*---------------------------------------------------------------------------*/
  190. /* menu command and menu picks */
  191. /*---------------------------------------------------------------------------*/
  192.  
  193. MenuPickType MenuPick(short id, short item)
  194. {
  195.     const MenuPickType *mt = NULL;
  196.     const MenuPickType *found = NULL;
  197.     MenuPickType pick;
  198.     MenuCommandType cmd;
  199.     
  200.     memclr(&pick, sizeof(pick));
  201.     mt = *gMenuTable; /* dereferenced handle! */
  202.     for (cmd = 0; cmd < gMenuTableSize; cmd++) {
  203.         if (mt[cmd].id == id) {
  204.             if (! found)
  205.                 found = mt + cmd;
  206.             if (mt[cmd].item == item) {
  207.                 found = mt + cmd;
  208.                 break;
  209.             }
  210.         }
  211.     }
  212.     if (found) {
  213.         pick = *found;
  214.         pick.item = item;
  215.     }
  216.     ensure(found ? (MenuPickValid(&pick) && MenuValid(pick.menu)) : true);
  217.     return(pick);
  218. }
  219.  
  220. const MenuPickType *MenuCmdPick(MenuCommandType cmd)
  221. {
  222.     require(MenuCmdValid(cmd));
  223.     return(&(*gMenuTable)[cmd]);
  224. }
  225.  
  226. short MenuCmdID(MenuCommandType cmd)
  227. {
  228.     require(MenuCmdValid(cmd));
  229.     return((*gMenuTable)[cmd].id);
  230. }
  231.  
  232. short MenuCmdItem(MenuCommandType cmd)
  233. {
  234.     require(MenuCmdValid(cmd));
  235.     return((*gMenuTable)[cmd].item);
  236. }
  237.  
  238. MenuHandle MenuCmdHandle(MenuCommandType cmd)
  239. {
  240.     require(MenuCmdValid(cmd));
  241.     return((*gMenuTable)[cmd].menu);
  242. }
  243.  
  244. void MenuCmdEnable(MenuCommandType cmd)
  245. {
  246.     MenuEnable(MenuCmdHandle(cmd), MenuCmdItem(cmd), true);
  247. }
  248.  
  249. void MenuCmdDisable(MenuCommandType cmd)
  250. {
  251.     MenuEnable(MenuCmdHandle(cmd), MenuCmdItem(cmd), false);
  252. }
  253.  
  254. void MenuCmdCheck(MenuCommandType cmd, Boolean check)
  255. {
  256.     CheckItem(MenuCmdHandle(cmd), MenuCmdItem(cmd), check);
  257. }
  258.  
  259. /*---------------------------------------------------------------------------*/
  260. /* accessing menus */
  261. /*---------------------------------------------------------------------------*/
  262.  
  263. /* this is the same as GetMHandle, except that if the menu ID is
  264.     kHMHelpMenuID it returns the application's help menu (not the
  265.     global help menu) */
  266. MenuHandle MenuHandleGet(short id)
  267. {
  268.     MenuHandle helpmenu = NULL;
  269.     MenuHandle result = NULL;
  270.     
  271.     if (id == kHMHelpMenuID) {
  272.         if (MacHasHelpManager() && HMGetHelpMenuHandle(&helpmenu) == noErr)
  273.             result = helpmenu;
  274.     }
  275.     else
  276.         result = GetMHandle(id);
  277.     ensure(! result || MenuValid(result));
  278.     return(result);
  279. }
  280.  
  281. /* return item number with given title, or 0 if not found */
  282. short MenuFindItem(MenuHandle menu, const CStr255 title)
  283. {
  284.     short        item;        /* current item number */
  285.     short        nitems;    /* number of items in menu */
  286.     Str255    name;        /* name of current item */
  287.     Str255    ptitle;    /* title of item being searched for */
  288.     
  289.     require(MenuValid(menu));
  290.     require(StrValid(title, sizeof(CStr255)));
  291.     item = 0;
  292.     nitems = CountMItems(menu);
  293.     c2pstr(strcpy((char*)ptitle, title));
  294.     for (item = 1; item <= nitems; item++) {
  295.         GetItem(menu, item, name);
  296.         if (EqualString(ptitle, name, false, true))
  297.             break;
  298.     }
  299.     item = (item <= nitems ? item : 0);
  300.     ensure(item >= 0 && item <= CountMItems(menu));
  301.     return(item);
  302. }
  303.  
  304. /*---------------------------------------------------------------------------*/
  305. /* accessing the menu list */
  306. /*---------------------------------------------------------------------------*/
  307.  
  308. /* A handle to this structure is stored in the MeunList low memory global
  309.     (see Menu Manager chapter of IM-1) */
  310. typedef struct {
  311.     short offset;
  312.     short right;
  313.     short unused;
  314. } MenuListHdr;
  315.  
  316. /* return number of menus in the menu list */
  317. short MenuListCount(void)
  318. {
  319.     return((**(MenuListHdr **)GetMenuList()).offset / 6);
  320. }
  321.  
  322. /* Return a handle to the indexed menu in the menu list. The first
  323.     menu (normally the Apple menu) has index 0. If the indexed menu
  324.     doesn't exist NULL is returned. */
  325. MenuHandle MenuListGet(short index)
  326. {
  327.     struct list {
  328.         MenuHandle    menu;
  329.         short            left;
  330.     } *list;
  331.     MenuHandle menu = NULL;
  332.  
  333.     if (0 <= index && index < MenuListCount()) {
  334.         list = (struct list *) (*GetMenuList() + sizeof(MenuListHdr));
  335.         menu = list[index].menu;
  336.     }
  337.     return(menu);
  338. }
  339.  
  340. /*---------------------------------------------------------------------------*/
  341. /* enabling and disabling menus and adjusting menus */
  342. /*---------------------------------------------------------------------------*/
  343.  
  344. /* enable or disable the menu item */
  345. void MenuEnable(MenuHandle menu, short item, Boolean enable)
  346. {    
  347.     require(MenuValid(menu));
  348.     if (enable)
  349.         EnableItem(menu, item);
  350.     else
  351.         DisableItem(menu, item);
  352. }
  353.  
  354. /* true if the menu item is enabled */
  355. Boolean MenuEnabled(MenuHandle menu, short item)
  356. {
  357.     require(MenuValid(menu));
  358.     require(item >= 0 && item <= CountMItems(menu));
  359.     return(item > 31 || ((**menu).enableFlags & (1 << item)) != 0);
  360. }
  361.  
  362. /* Return a 32-bit flag for which the i'th bit is set if the i'th menu
  363.     is enabled. */
  364. long MenuBarState(void)
  365. {
  366.     short    i = 0;
  367.     short    mlc = 0;
  368.     long    flags = 0;
  369.     
  370.     mlc = min(sizeof(long) * CHAR_BIT, MenuListCount());
  371.     for (i = 0; i < mlc; i++) {
  372.         if (((**MenuListGet(i)).enableFlags & 0x01))
  373.             flags |= 1 << i;
  374.     }
  375.     return(flags);
  376. }
  377.  
  378. /* Enable menus for which the corresponding bit of 'flags' is set and disable
  379.     all other menus. */
  380. void MenuBarEnable(long flags)
  381. {
  382.     short    i = 0;
  383.     short    mlc = 0;
  384.     
  385.     mlc = min(sizeof(long) * CHAR_BIT, MenuListCount());
  386.     for (i = 0; i < mlc; i++)
  387.         MenuEnable(MenuListGet(i), 0, (flags & (1 << i)) != 0);
  388. }
  389.  
  390. /* uncheck all the items in the menu */
  391. static void MenuUncheck(MenuHandle menu)
  392. {
  393.     short i, nitems = CountMItems(menu);
  394.     
  395.     for (i = 1; i <= nitems; i++)
  396.         CheckItem(menu, i, false);
  397. }
  398.  
  399. /* disable all the menus and reset the undo item; call this before adjusting
  400.     the menus */
  401. void MenuAdjust(void)
  402. {
  403.     SignedByte state = 0;
  404.     const MenuPickType *table;
  405.     MenuCommandType cmd;
  406.     Str255 title;
  407.  
  408.     state = HandleLockHi(gMenuTable);
  409.     table = *gMenuTable;
  410.     for (cmd = 0; cmd < gMenuTableSize; cmd++) {
  411.         if (table[cmd].menu) {
  412.             DisableItem(table[cmd].menu, table[cmd].item);
  413.             CheckItem(table[cmd].menu, table[cmd].item, false);
  414.         }
  415.     }
  416.     if (table[CMD_FONT].menu) MenuUncheck(table[CMD_FONT].menu);
  417.     if (table[CMD_SIZE].menu) MenuUncheck(table[CMD_SIZE].menu);
  418.     if (table[CMD_STYLE].menu) MenuUncheck(table[CMD_STYLE].menu);
  419.     if (table[CMD_WINDOW].menu) MenuUncheck(table[CMD_WINDOW].menu);
  420.     if (table[CMD_UNDO].menu) {
  421.         GetIndString(title, RLS_UNDO, 1);
  422.         SetItem(table[CMD_UNDO].menu, table[CMD_UNDO].item, title);
  423.     }
  424.     HandleRestore(gMenuTable, state);
  425. }
  426.  
  427. /*    Given a font family id and true in outlined, will outline all items in a size
  428.     menu which actually exist in that font. If outlined is false, all items will
  429.     be set to plain text, which is useful if you don't have any font info. */
  430. void MenuOutlineSizes(MenuHandle menu, short family, Boolean outlined)
  431. {
  432.     int        nitems;    /* number of items in menu */
  433.     int        item;        /* current item number */
  434.     long        size;        /* size of current menu item */
  435.     Str255    name;        /* name of item */
  436.     Boolean    found;    /* flag that we've already found the menu item */
  437.  
  438.     require(MenuValid(menu));
  439.     found = false;
  440.     nitems = CountMItems(menu);
  441.     for (item = 1; item <= nitems; item++) {
  442.         GetItem(menu, item, name);
  443.         StringToNum(name, &size);
  444.         if (outlined && RealFont(family, size))
  445.             SetItemStyle(menu, item, outline);
  446.         else
  447.             SetItemStyle(menu, item, 0);
  448.     }
  449. }
  450.  
  451. /*---------------------------------------------------------------------------*/
  452. /* setting up menus */
  453. /*---------------------------------------------------------------------------*/
  454.  
  455. /* Append the menu with the specified ID to the menu bar. After the menu
  456.     is inserted into the menu list all heirarchical menus it contains are
  457.     inserted. */
  458. void MenuAppend(short id)
  459. {
  460.     MenuHandle menu = NULL;    /* current menu */
  461.     MenuHandle heir = NULL;    /* heirarchical menu */
  462.     short nitems = 0;            /* number of items in current menu */
  463.     short item = 0;            /* current item number */
  464.     short cmd = 0;                /* item's command character */
  465.     short mark = 0;            /* item's mark character */
  466.     
  467.     /* load next menu and ensure it's unpurgeable */
  468.     menu = (MenuHandle) ResGet('MENU', id);
  469.     HNoPurge((Handle) menu);
  470.     menu = GetMenu(id);
  471.     FailNILRes(menu);
  472.  
  473.     /* append menu */
  474.     InsertMenu(menu, 0);
  475.     
  476.     /* search for and insert all heirarchical menus */
  477.     nitems = CountMItems(menu);
  478.     for (item = 1; item <= nitems; item++) {
  479.         GetItemCmd(menu, item, &cmd);
  480.         if (cmd == hMenuCmd) {
  481.             GetItemMark(menu, item, &mark);
  482.             heir = GetMenu(mark);
  483.             FailNILRes(heir);
  484.             HNoPurge((Handle) heir);
  485.             InsertMenu(heir, -1);
  486.         }
  487.     }
  488. }
  489.  
  490. /* Create the menu bar. This should be very similar to the ToolBox routine
  491.     GetNewMBar followed by a call to SetMenuBar. This function differs from
  492.     GetNewMBar in that all 'DRVR' resources are inserted into the apple
  493.     menu (assumed to be the first menu in the menu bar) and all heirarchical
  494.     menus are automatically created and inserted into the menu list. */
  495. void MenuBarGet(short id)
  496. {
  497.     short        nmenus = 0;    /* number of menus */
  498.     short        index = 0;    /* index to menus */
  499.     volatile Handle    mbar = NULL;    /* the menu bar resource */
  500.     volatile SignedByte state = 0;    /* saved state of resource handle */
  501.     
  502.     TRY {
  503.  
  504.         /* build menu bar */
  505.         mbar = ResGet('MBAR', id);
  506.         state = HandleNoPurge(mbar);
  507.         nmenus = ((short *) *mbar)[0];
  508.         for (index = 1; index <= nmenus; index++)
  509.             MenuAppend(((short *) *mbar)[index]);
  510.  
  511.         /* build apple and font menus */        
  512.         if (GetMHandle(RLM_APPLE))
  513.             AddResMenu(GetMHandle(RLM_APPLE), 'DRVR');
  514.         if (GetMHandle(RLM_FONT))    
  515.             AddResMenu(GetMHandle(RLM_FONT), 'FOND');
  516.             
  517.         #ifndef NDEBUG
  518.             /* append debug menu */
  519.             if (RELEASE_LEVEL == RELEASE_DEV || KeyIsDown(optionKeyCode))
  520.                 MenuAppend(RLM_DEBUG);
  521.         #endif /* NDEBUG */
  522.     } CLEANUP {
  523.         if (mbar)
  524.             HandleRestore(mbar, state);
  525.     } ENDTRY;
  526. }
  527.